<?php
// +----------------------------------------------------------------------
// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2014 http://www.thinkcmf.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: Dean <zxxjjforever@163.com>
// +----------------------------------------------------------------------

namespace app\appapi\controller;

use cmf\controller\HomeBaseController;
use think\facade\Db;

/**
 * 支付回调
 */
class PayController extends HomebaseController
{
	
	//支付宝 回调
	public function notify_ali()
	{
		$configpri = getConfigPri();
		require_once(CMF_ROOT."sdk/alipay_app/alipay.config.php");
		$alipay_config['partner'] = $configpri['aliapp_partner'];
		require_once(CMF_ROOT."sdk/alipay_app/lib/alipay_core.function.php");
		require_once(CMF_ROOT."sdk/alipay_app/lib/alipay_rsa.function.php");
		require_once(CMF_ROOT."sdk/alipay_app/lib/alipay_notify.class.php");
		
		//计算得出通知验证结果
		$alipayNotify  = new \AlipayNotify($alipay_config);
		$verify_result = $alipayNotify->verifyNotify();
		$this->logali("ali_data:".json_encode($_POST));
		if($verify_result){//验证成功
			//商户订单号
			$out_trade_no = $_POST['out_trade_no'];
			//支付宝交易号
			$trade_no = $_POST['trade_no'];
			//交易状态
			$trade_status = $_POST['trade_status'];
			//交易金额
			$total_fee = $_POST['total_fee'];
			if($_POST['trade_status'] == 'TRADE_FINISHED'){
				//判断该笔订单是否在商户网站中已经做过处理
				//如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				//如果有做过处理，不执行商户的业务程序
				
				//注意：
				//退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
				//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
				
				//调试用，写文本函数记录程序运行情况是否正常
				//logResult("这里写入想要调试的代码变量值，或其他运行的结果记录");
				
			} elseif($_POST['trade_status'] == 'TRADE_SUCCESS'){
				//判断该笔订单是否在商户网站中已经做过处理
				//如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				//如果有做过处理，不执行商户的业务程序
				
				//注意：
				//付款完成后，支付宝系统发送该交易状态通知
				//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
				
				//调试用，写文本函数记录程序运行情况是否正常
				//logResult("这里写入想要调试的代码变量值，或其他运行的结果记录");
				$where['orderno'] = $out_trade_no;
				$where['money']   = $total_fee;
				$where['type']    = 1;
				
				$data = ['trade_no' => $trade_no];
				$this->logali("where:".json_encode($where));
				$res = handelCharge($where, $data);
				if($res == 0){
					$this->logali("orderno:".$out_trade_no.' 订单信息不存在');
					echo "fail";
					
					return;
				}
				
				$this->logali("成功");
				echo "success";        //请不要修改或删除
				
				return;
			}
			//——请根据您的业务逻辑来编写程序（以上代码仅作参考）——
			
			echo "fail";        //请不要修改或删除
			
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		} else{
			$this->logali("验证失败");
			//验证失败
			echo "fail";
			
			//调试用，写文本函数记录程序运行情况是否正常
			//logResult("这里写入想要调试的代码变量值，或其他运行的结果记录");
		}
		
	}
	/* 支付宝支付 */
	
	/* 微信支付 */
	private $wxDate = null;
	
	public function notify_wx()
	{
		$config = getConfigPri();
		//$xmlInfo = $GLOBALS['HTTP_RAW_POST_DATA'];
		$xmlInfo = file_get_contents("php://input");
		//解析xml
		$arrayInfo    = $this->xmlToArray($xmlInfo);
		$this->wxDate = $arrayInfo;
		$this->logwx("wx_data:".json_encode($arrayInfo));//log打印保存
		if($arrayInfo['return_code'] == "SUCCESS"){
			// if(isset($arrayInfo['return_msg']) && $arrayInfo['return_msg'] != null){
			// 	echo $this -> returnInfo("FAIL","签名失败");
			// 	$this -> logwx("签名失败:".$sign);//log打印保存
			//
			// }else{
			$wxSign = $arrayInfo['sign'];
			unset($arrayInfo['sign']);
			$arrayInfo['appid']  = $config['wx_appid'];
			$arrayInfo['mch_id'] = $config['wx_mchid'];
			$key                 = $config['wx_key'];
			ksort($arrayInfo);//按照字典排序参数数组
			$sign = $this->sign($arrayInfo, $key);//生成签名
			$this->logwx("数据打印测试签名signmy:".$sign.":::微信sign:".$wxSign);//log打印保存
			if($this->checkSign($wxSign, $sign)){
				echo $this->returnInfo("SUCCESS", "OK");
				$this->logwx("签名验证结果成功:".$sign);//log打印保存
				$this->orderServer();//订单处理业务逻辑
				
			} else{
				echo $this->returnInfo("FAIL", "签名失败");
				$this->logwx("签名验证结果失败:本地加密：".$sign.'：：：：：三方加密'.$wxSign);//log打印保存
			}
			//}
		} else{
			echo $this->returnInfo("FAIL", "签名失败");
			$this->logwx($arrayInfo['return_code']);//log打印保存
			
		}
	}
	
	private function returnInfo($type, $msg)
	{
		if($type == "SUCCESS"){
			return $returnXml = "<xml><return_code><![CDATA[{$type}]]></return_code></xml>";
		} else{
			return $returnXml = "<xml><return_code><![CDATA[{$type}]]></return_code><return_msg><![CDATA[{$msg}]]></return_msg></xml>";
		}
	}
	
	// 杉徳线上支付
	public function notify_sand()
	{
		$config = getConfigPri();
		//$xmlInfo1 = file_get_contents("php://input");  // 获取成功
		//$this->logSand("返回数据1:".json_encode($xmlInfo1)); //log打印保存
		//$xmlInfo2 = $GLOBALS['HTTP_RAW_POST_DATA'];    // 获取失败
		//$this->logSand("返回数据2:".json_encode($xmlInfo2)); //log打印保存
		$xmlInfo3 = $_POST;  // 获取成功
		//$this->logSand("返回数据3:".json_encode($xmlInfo3)); //log打印保存
		
		/*$xmlInfo3 = array(
			'respDesc'  => '成功',
			'respTime'  => '2024-05-07 15:47:07',
			'sign'      => 'l3eXWmiq23m/yH2fi6ezlSLqkI0MxHccKwFIhMhhdrprgLpm3lzKPZAp+ooOPye7lSx59/zPEsKYDn5f7goD0lT9KXWajrhz+w9Eb2yaNRz2oBCGZ4rF9OstQ68U6AzyBB10QiwSKxvX0Se3stmdRt+CuuWulQCodbuYN0kTDqvfI+tpDhIeHax+X7tjZtD010qqy2cSFLkygxUJBWjUpRNZXx4iXxeTzFabFK1lbiFv7Eac8UiBKF75+BpYnTV0bEr0mbFt21UahHe2NRefu4MWtAvBCaFeXZITscDxwPbbpzmTZkFWm4Ql2/T9sljHaBL33gmZEN2IAWtJXR04+g==',
			'signType'  => 'RSA',
			'accessMid' => '68888TS125511',
			'bizData'   => '{"marketProduct":"CSDB","amount":"0.01","payMode":"JSAPI","plFeeAmt":"0.00","mid":"68888TS125511","orderStatus":"success","sandSerialNo":"240507154207031000000203","channelOrderNo":"20240507001365910000000000000026","outRespTime":"2024-05-07 15:47:07","eventType":"recv","outOrderNo":"Sandtest1075_20240507154658786","payer":{"payerLogonNo":"","payerAccNo":"","payerAccType":"alipay","signNo":""},"feeAmt":"0.00","extraFeeAmt":"0.00","settleInfo":{"mhtAccDate":"20240507","expSettleAmt":"0.01"},"resultStatus":"success","payType":"ALIPAY","discountInfo":{"issuerSubsidyAmt":"0.00","activityNo":"","channelSubsidyAmt":"0.00","sandSubsidyAmt":"0.00"},"channelFinishTime":"20230209142800","buyerPayAmt":"0.01","holidayFeeAmt":"0.00","channelSerialNo":"932023020922001460751418431425","reqReserved":{"reqMemo":"全支付收银台自用摘要"},"finishedTime":"20240507154706"}',
			'version'   => '4.0.0',
			'respCode'  => 'success',
		);*/
		
		//解析xml
		//$arrayInfo = $this->xmlToArray($xmlInfo);
		$arrayInfo = $xmlInfo3;
		//$this->logSand("异步报文：".json_encode($xmlInfo3, JSON_UNESCAPED_UNICODE)); //log打印保存
		//$this->logSand("返回数据:".var_export($arrayInfo, 1)); //log打印保存
		if(empty($arrayInfo)){
			echo $this->returnInfo("FAIL", "签名失败");
			
			return;
		}
		if($arrayInfo['respCode'] != "success"){
			echo $this->returnInfo("FAIL", "签名失败");
			$this->logSand($arrayInfo['return_code']);//log打印保存
			
			return;
		}
		
		// 验证签名
		$ds       = DIRECTORY_SEPARATOR;
		$basePath = app()->getRootPath().'PhalApi'.$ds.'Appapi'.$ds.'Domain'.$ds.'sand'.$ds;
		if(strpos($_SERVER['SERVER_NAME'], 'zhiboxiu.com.cn') === false){
			//测试环境
			$publicKeyPath  = $basePath."sand-test.cer";
			$privateKeyPath = $basePath."sanduatprikey.pfx";
			$privateKeyPwd  = "123456";
			$URL            = "https://scfp-uat.sand.com.cn/gateway/trade";  // 测试
			$mid            = '68888TS125511';
			
			// 第二个测试环境
			$publicKeyPath  = $basePath."sand_pro.cer";
			$privateKeyPath = $basePath."smsc20240509.pfx";
			$privateKeyPwd  = "135700";
			$URL            = "https://scfp-uat.sand.com.cn/gateway/trade";  // 测试
			$mid            = '6888804125511';
		} else{
			//生产环境
			$publicKeyPath  = $basePath."sc20240509zb.cer";
			$publicKeyPath  = $basePath."sand_pro.cer";
			$privateKeyPath = $basePath."smsc20240509.pfx";
			$privateKeyPwd  = "135700";
			$URL            = "https://cashapi.sandpay.com.cn/gateway/trade";  // 正式
			$mid            = '6888804125511';
		}
		
		$this->logSand('公钥位置'.$publicKeyPath);//log打印保存
		// 验签
		// step6: 使用公钥验签报文$decryptPlainText
		$publickey = $this->sand_loadX509Cert($publicKeyPath);
		$result    = $this->sand_verify($arrayInfo['bizData'], $arrayInfo['sign'], $publickey);
		$this->logSand('验签结果'.$result);//log打印保存
		if(empty($result)){
			echo $this->returnInfo("FAIL", "签名失败");
			$this->logSand('签名失败');//log打印保存
			//$this->logSand('公钥：'.$publicKeyPath);//log打印保存
			
			return;
		}
		
		// 签名成功
		echo $this->returnInfo("SUCCESS", "OK");
		$this->logSand("签名验证结果成功");//log打印保存
		
		$bizData  = is_array($arrayInfo['bizData']) ? $arrayInfo['bizData'] : json_decode($arrayInfo['bizData'], true);
		$trade_no = $bizData['outOrderNo'];
		$this->logSand('trade_no：'.$trade_no);//log打印保存
		
		$where = ['orderno' => $trade_no, 'type' => 7];
		$data  = ['trade_no' => $trade_no];
		$this->logSand("where:".json_encode($where));
		$res = handelCharge($where, $data);
		$this->logSand("结果处理".$res);//log打印保存
		if($res == 0){
			$this->logSand("orderno:".$trade_no.' 订单信息不存在');
			
			return false;
		}
		
		$this->logSand("成功");
		$this->logSand("");
		
		//return true;
	}
	
	
	//签名验证
	private function checkSign($sign1, $sign2)
	{
		return trim($sign1) == trim($sign2);
	}
	
	/* 订单查询加值业务处理
	 * @param orderNum 订单号	   
	 */
	private function orderServer()
	{
		$info = $this->wxDate;
		$this->logwx("info:".json_encode($info));
		$where['orderno'] = $info['out_trade_no'];
		$where['type']    = 2;
		
		$trade_no = $info['transaction_id'];
		$data     = ['trade_no' => $trade_no];
		$this->logwx("where:".json_encode($where));
		$res = handelCharge($where, $data);
		if($res == 0){
			$this->logwx("orderno:".$out_trade_no.' 订单信息不存在');
			
			return false;
		}
		
		$this->logwx("成功");
		
		return true;
	}
	
	/**
	 * sign拼装获取
	 */
	private function sign($param, $key)
	{
		$sign = "";
		foreach($param as $k => $v){
			$sign .= $k."=".$v."&";
		}
		
		$sign .= "key=".$key;
		$sign = strtoupper(md5($sign));
		
		return $sign;
		
	}
	
	/**
	 * xml转为数组
	 */
	private function xmlToArray($xmlStr)
	{
		$msg     = array();
		$postStr = $xmlStr;
		$msg     = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
		
		return $msg;
	}
	
	/* 微信支付 */
	
	/* 苹果支付 */
	public function notify_ios()
	{
		$content = file_get_contents("php://input");
		$data    = json_decode($content, true);
		$this->logios("data:".json_encode($data));
		
		$receipt      = $data["receipt-data"] ?? '';
		$isSandbox    = $data["sandbox"] ?? '0';
		$out_trade_no = $data["out_trade_no"] ?? '';
		$version_ios  = $data["version_ios"] ?? '';
		
		$info = $this->getReceiptData($receipt, $version_ios);
		$this->logios("info:".json_encode($info));
		
		$iforderinfo = Db::name("charge_user")->where(["trade_no" => $info['transaction_id'], "type" => '3'])->find();
		
		if($iforderinfo){
			echo '{"status":"fail","info":"非法提交-001"}';
			
			return;
		}
		
		$chargeinfo = Db::name("charge_rules")->where(["product_id" => $info['product_id']])->find();
		if(!$chargeinfo){
			echo '{"status":"fail","info":"非法提交-002"}';
			
			return;
		}
		
		//判断订单是否存在
		$where['orderno'] = $out_trade_no;
		$where['coin']    = $chargeinfo['coin_ios'];
		$where['type']    = 3;
		
		$trade_no = $info['transaction_id'];
		$ambient  = $info['ambient'];
		$data     = ['trade_no' => $trade_no, 'ambient' => $ambient,];
		$this->logios("where:".json_encode($where));
		
		$res = handelCharge($where, $data);
		if($res == 0){
			$this->logios("orderno:".$out_trade_no.' 订单信息不存在');
			echo '{"status":"fail","info":"订单信息不存在-003"}';
			
			return;
		}
		
		$this->logios("成功");
		echo '{"status":"success","info":"充值支付成功"}';
	}
	
	
	public function getReceiptData($receipt, $version_ios)
	{
		$config      = getConfigPub();
		$ios_shelves = $config['ios_shelves'];
		$this->logios("version_ios:".$version_ios);
		$this->logios("ios_shelves:".$ios_shelves);
		
		$ambient = 0;
		if($version_ios == $ios_shelves){
			//沙盒
			$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';
			$ambient  = 0;
		} else{
			//生产
			$endpoint = 'https://buy.itunes.apple.com/verifyReceipt';
			$ambient  = 1;
		}
		
		$postData = json_encode(array('receipt-data' => $receipt));
		$ch       = curl_init($endpoint);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);    //关闭安全验证
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);    //关闭安全验证
		curl_setopt($ch, CURLOPT_POST, true);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
		
		$response = curl_exec($ch);
		$errno    = curl_errno($ch);
		$errmsg   = curl_error($ch);
		curl_close($ch);
		
		$this->logios("getReceiptData response:".json_encode($response));
		$this->logios("getReceiptData errno:".json_encode($errno));
		$this->logios("getReceiptData errmsg:".json_encode($errmsg));
		if($errno != 0){
			echo '{"status":"fail","info":"服务器出错，请联系管理员"}';
			
			return;
		}
		
		$data = json_decode($response, 1);
		if(!is_array($data)){
			echo '{"status":"fail","info":"验证失败,如有疑问请联系管理"}';
			
			return;
		}
		
		if(!isset($data['status']) || $data['status'] != 0){
			echo '{"status":"fail","info":"验证失败,如有疑问请联系管理"}';
			
			return;
		}
		
		$newdata = end($data['receipt']['in_app']);
		
		return array('product_id' => $newdata['product_id'], 'transaction_id' => $newdata['transaction_id'], 'ambient' => $ambient,);
	}
	
	/* 苹果支付 */
	
	//微信小程序支付回调
	private $wxMiniDate = null;
	
	public function notify_wx_mini()
	{
		$xmlInfo = file_get_contents("php://input");
		//解析xml
		$arrayInfo = $this->xmlToArray($xmlInfo);
		
		$this->wxMiniDate = $arrayInfo;
		$this->logwx_mini("wxmini_data:".json_encode($arrayInfo));//log打印保存
		
		$config = getConfigPri();
		if($arrayInfo['return_code'] == "SUCCESS"){
			$wxSign = $arrayInfo['sign']; //支付返回的签名
			unset($arrayInfo['sign']);
			$arrayInfo['appid']  = $config['wx_mini_appid'];
			$arrayInfo['mch_id'] = $config['wx_mini_mchid'];
			$key                 = $config['wx_mini_key'];
			ksort($arrayInfo);//按照字典排序参数数组
			
			$sign = $this->sign($arrayInfo, $key);//生成签名
			$this->logwx_mini("数据打印对比签名signmy:".$sign.":::微信sign:".$wxSign);//log打印保存
			if($this->checkSign($wxSign, $sign)){
				echo $this->returnInfo("SUCCESS", "OK");
				$this->logwx_mini("签名验证结果成功:".$sign);//log打印保存
				$this->wxMiniOrderServer();//微信小程序订单处理业务逻辑
			} else{
				echo $this->returnInfo("FAIL", "签名失败");
				$this->logwx_mini("签名验证结果失败:本地加密：".$sign.'：：：：：三方加密'.$wxSign);//log打印保存
			}
		} else{
			echo $this->returnInfo("FAIL", "签名失败");
			$this->logwx_mini($arrayInfo['return_code']);//log打印保存
		}
	}
	
	/* 微信小程序订单查询加值业务处理
	 * @param orderNum 订单号	   
	 */
	private function wxMiniOrderServer()
	{
		$info = $this->wxMiniDate;
		$this->logwx_mini("wxMiniOrderServer info:".json_encode($info));
		$out_trade_no     = $info['out_trade_no'];
		$where['orderno'] = $info['out_trade_no'];
		$where['type']    = 4;
		
		$trade_no = $info['transaction_id']; //微信支付订单号
		$data     = ['trade_no' => $trade_no];
		$this->logwx_mini("where:".json_encode($where));
		$res = handelCharge($where, $data);
		if($res == 0){
			$this->logwx_mini("orderno:".$out_trade_no.' 订单信息不存在');
			
			return false;
		}
		
		$this->logwx_mini("成功");
		
		return true;
	}
	
	/* 打印log */
	public function logali($msg)
	{
		file_put_contents(CMF_ROOT.'log/think/appapi/pay/logali_'.date('Y-m-d').'.txt', date('Y-m-d H:i:s').'  msg:'.$msg."\r\n", FILE_APPEND);
	}
	
	/* 打印log */
	public function logwx($msg)
	{
		file_put_contents(CMF_ROOT.'log/think/appapi/pay/logwx_'.date('Y-m-d').'.txt', date('Y-m-d H:i:s').'  msg:'.$msg."\r\n", FILE_APPEND);
	}
	
	/* 打印log */
	public function logSand($msg)
	{
		file_put_contents(CMF_ROOT.'log/think/appapi/pay/logsand_'.date('Y-m-d').'.txt', date('Y-m-d H:i:s').'  msg:'.$msg."\r\n", FILE_APPEND);
	}
	
	/* 打印log */
	public function logios($msg)
	{
		file_put_contents(CMF_ROOT.'log/think/appapi/pay/logios_'.date('Y-m-d').'.txt', date('Y-m-d H:i:s').'  msg:'.$msg."\r\n", FILE_APPEND);
	}
	
	/* 打印log */
	public function logwx_mini($msg)
	{
		file_put_contents(CMF_ROOT.'log/think/appapi/pay/logwx_mini_'.date('Y-m-d').'.txt', date('Y-m-d H:i:s').'  msg:'.$msg."\r\n", FILE_APPEND);
	}
	
	//公钥pem
	protected function sand_loadX509Cert($path)
	{
		try{
			$file = file_get_contents($path);
			if(!$file){
				throw new \Exception('loadx509Cert::file_get_contents ERROR');
			}
			$cert   = chunk_split(base64_encode($file), 64, "\n");
			$cert   = "-----BEGIN CERTIFICATE-----\n".$cert."-----END CERTIFICATE-----\n";
			$res    = openssl_pkey_get_public($cert);
			$detail = openssl_pkey_get_details($res);
			openssl_free_key($res);
			
			if(!$detail){
				throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
			}
			
			return $detail['key'];
		} catch(\Exception $e){
			//throw $e;
			return false;
		}
	}
	
	//sha256验签方法
	protected function sand_verify($plainText, $sign, $path)
	{
		$resource = openssl_pkey_get_public($path);
		$result   = openssl_verify($plainText, base64_decode($sign), $resource, 'SHA256');
		//在PHP8.0以后，即时编译（JIT）模式, 性能已经提高了90%, 你不再需要手动释放openssl_free_key()资源。
		//PHP的垃圾回收机制会自动处理这些内存管理问题。所以，你不需要担心内存占用的问题。
		openssl_free_key($resource);
		
		/*var_dump('校验结果===========');
		if($result == 1){
			$result = '验签成功1';
		} else{
			$result = '验签失败';
		}
		var_dump($result);*/
		
		return $result;
	}
	
}


